
  const template = `<div><p>Vue</p><p>Template</p></div>`
  
  const State = {
    initial: 1,
    tagOpen: 2,
    tagName: 3,
    text: 4,
    tagEnd: 5,
    tagEndName: 6
  }
  
  function isAlpha(char) {
    return char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z'
  }
  
  function tokenize(str) {
    let currentState = State.initial
    const chars = []
    const tokens = []
    while(str) {
      const char = str[0]
      switch (currentState) {
        case State.initial:
          if (char === '<') {
            currentState = State.tagOpen
            str = str.slice(1)
          } else if (isAlpha(char)) {
            currentState = State.text
            chars.push(char)
            str = str.slice(1)
          }
          break
        case State.tagOpen:
          if (isAlpha(char)) {
            currentState = State.tagName
            chars.push(char)
            str = str.slice(1)
          } else if (char === '/') {
            currentState = State.tagEnd
            str = str.slice(1)
          }
          break
        case State.tagName:
          if (isAlpha(char)) {
            chars.push(char)
            str = str.slice(1)
          } else if (char === '>') {
            currentState = State.initial
            tokens.push({
              type: 'tag',
              name: chars.join('')
            })
            chars.length = 0
            str = str.slice(1)
          }
          break
        case State.text:
          if (isAlpha(char)) {
            chars.push(char)
            str = str.slice(1)
          } else if (char === '<') {
            currentState = State.tagOpen
            tokens.push({
              type: 'text',
              content: chars.join('')
            })
            chars.length = 0
            str = str.slice(1)
          }
          break
        case State.tagEnd:
          if (isAlpha(char)) {
            currentState = State.tagEndName
            chars.push(char)
            str = str.slice(1)
          }
          break
        case State.tagEndName:
          if (isAlpha(char)) {
            chars.push(char)
            str = str.slice(1)
          } else if (char === '>') {
            currentState = State.initial
            tokens.push({
              type: 'tagEnd',
              name: chars.join('')
            })
            chars.length = 0
            str = str.slice(1)
          }
          break
      }
    }
  
    return tokens
  }
  
  function parse(str) {
    const tokens = tokenize(str)

    const root = {
      type: 'Root',
      children: []
    }
    const elementStack = [root]

    while (tokens.length) {
      const parent = elementStack[elementStack.length - 1]
      const t = tokens[0]
      switch (t.type) {
        case 'tag':
          const elementNode = {
            type: 'Element',
            tag: t.name,
            children: []
          }
          parent.children.push(elementNode)
          elementStack.push(elementNode)
          break
        case 'text':
          const textNode = {
            type: 'Text',
            content: t.content
          }
          parent.children.push(textNode)
          break
        case 'tagEnd':
          elementStack.pop()
          break
      }
      tokens.shift()
    }

    return root
  }

  function dump(node, indent = 0) {
    const type = node.type
    const desc = node.type === 'Root'
      ? ''
      : node.type === 'Element'
        ? node.tag
        : node.content

    console.log(`${'-'.repeat(indent)}${type}: ${desc}`)

    if (node.children) {
      node.children.forEach(n => dump(n, indent + 2))
    }
  }
  

  function traverseNode(ast) {
    const currentNode = ast

    if (currentNode.type === 'Element' && currentNode.tag === 'p') {
      currentNode.tag = 'h1'
    }

    if (currentNode.type === 'Text') {
      currentNode.content = currentNode.content.repeat(2)
    }


    const children = currentNode.children
    if (children) {
      for (let i = 0; i < children.length; i++) {
        traverseNode(children[i])
      }
    }

    return ast
  }

  function transform(ast) {
    // 调用 traverseNode 完成转换
    traverseNode(ast)
    // 打印 AST 信息
    console.log(dump(ast))
  }

  const ast = parse(template)
  transform(ast)
